#include "wstr.h"

#include <sstream>

namespace wstr {

std::wstring toWide(std::string str) {
#ifdef WIN32
	// mbstow
	wchar_t *wstrarray = new wchar_t[str.length()+1];
	int errnum = MultiByteToWideChar(
		CP_UTF8,
		0,
		str.c_str(), -1,
		wstrarray, str.length()+1
	);
	if(errnum == 0) {
		delete[] wstrarray;
		return L"<ToWide: Can't convert>";
	}
	std::wstring wstr = wstrarray;
	delete[] wstrarray;
	return wstr;
#else
	// mbstow with unrepresentable -> L'?'
	wchar_t *dest = new wchar_t[str.length() + 1];
	int dest_index = 0;

	const char *str_array = str.c_str();
	int str_index = 0;
	int str_size = str.length() + 1;

	while(str_index < str_size) {
		int numread = mbtowc(dest + dest_index, str_array + str_index, str_size - str_index);
		if(numread == -1) {
			// Couldn't convert
			dest[dest_index] = L'?';
			dest_index++;
			str_index++;
		} else if(numread > 0) {
			// Converted
			dest_index++;
			str_index += numread;
		} else {
			// '\0' encountered, shouldn't happen
			break;
		}
	}
	dest[dest_index] = 0;
	std::wstring wstr = dest;
	delete[] dest;
	return wstr;
#endif
}

std::string toNonWide(std::wstring wstr) {
#ifdef WIN32
	// wctombs with unrepresentable -> '?'
	char *strarray = new char[wstr.length()*4+1];
	int errnum = WideCharToMultiByte(
		CP_UTF8,
		0,
		wstr.c_str(), -1,
		strarray, wstr.length()*4+1,
		NULL, NULL
	);
	if(errnum == 0) {
		delete[] strarray;
		return "<ToNonWide: Can't convert>";
	}
	std::string str = strarray;
	delete[] strarray;
	return str;
#else
	// wctombs with unrepresentable -> '?'
	char *dest = new char[wstr.length()*MB_CUR_MAX + 1];
	char *s = dest;
	for(int i=0; i<wstr.length(); ++i) {
		int numwritten = wctomb(s, wstr[i]);
		if(numwritten == -1) {
			*s = '?';
			++s;
		} else {
			s += numwritten;
		}
	}
	*s = 0;
	std::string str = dest;
	delete[] dest;
	return str;
#endif
}

// Convert to integer
bool isNum(wchar_t c) {
	return (c >= L'0' && c <= L'9');
}
bool isSgn(wchar_t c) {
	return (c == L'+' || c == L'-');
}
int toInt(std::wstring str) {
	// Check that str is numeric
	for(unsigned int i=0; i<str.size(); ++i) {
		if(!isNum(str[i]) && !(i==0 && isSgn(str[i]))) {
			throw ConversionError(L"Integer conversion error");
		}
	}
	// Convert
	int i;
	std::wstringstream ss;
	ss << str;
	ss >> i;
	if(ss.fail()) throw ConversionError(L"Integer conversion error");
	return i;
}


std::string strip(std::string str, const char *wspace) {
	size_t start = str.find_first_not_of(wspace);
	size_t end = str.find_first_of(wspace, start);
	
	size_t len;
	if(end == str.npos) {
		len = str.npos;
	} else {
		len = end-start;
	}

	if(start < end) {
		return str.substr(start, len);
	}
	return "";
}

WStringPartition partition(std::wstring str, const wchar_t *sep) {
	WStringPartition parts;
	size_t first_sep;
	
	first_sep = str.find_first_of(sep);
	if(first_sep == str.npos) {
		parts.head = str;
		parts.sep = L"";
		parts.tail = L"";
		return parts;
	}

	parts.head = str.substr(0, first_sep);
	parts.sep = str.substr(first_sep, 1);
	parts.tail = str.substr(first_sep+1, str.npos);
	return parts;
}

std::vector<std::wstring> split(std::wstring str, const wchar_t *sep) {
	std::wstring tail = str;
	std::vector<std::wstring> parts;

	while(!tail.empty()) {
		size_t first_sep = tail.find_first_of(sep);
		if(first_sep == tail.npos) {
			parts.push_back(tail);
			break;
		}

		std::wstring part = tail.substr(0, first_sep);
		tail.erase(tail.begin(), tail.begin() + first_sep+1);
		parts.push_back(part);
	}
	return parts;
}


}

const std::wstring W(const wchar_t *wstr) {
	return std::wstring(wstr);
}
const std::wstring W(const char *str) {
	return wstr::toWide(str);
}

std::wstring operator+(std::wstring &wstr1, std::wstring &wstr2) {
	std::wstring w = wstr1;
	w.append(wstr2);
	return w;
}
std::wstring operator+(std::wstring &wstr1, const wchar_t *wstr2) {
	return wstr1 + std::wstring(wstr2);
}
std::wstring operator+(const wchar_t *wstr1, std::wstring &wstr2) {
	return std::wstring(wstr1) + wstr2;
}

